
#ifndef __pmodelH__
#define __pmodelH__

#include "physics.h"

namespace WtfEngine {
	namespace PhysicalModels
	{
#ifdef __PHYSICS_INTERNAL__

		class Sphere : public PhysicalModel
		{
		public:
			Sphere(tScalar r, unsigned detail = 0);
		};

		class Oblong : public PhysicalModel
		{
		public:
			Oblong(const tVector& dim)
				: PhysicalModel(dim.length()){
				tVector d(dim / 2);
				
				//
				// Create vertices
				//

				mzLocalVertex = new tVector[8];
				mzLocalVertex[0] = d; d[2] = -d[2];
				mzLocalVertex[1] = d; d[1] = -d[1];
				mzLocalVertex[2] = d; d[2] = -d[2];
				mzLocalVertex[3] = d; d[0] = -d[0];
				mzLocalVertex[4] = d; d[2] = -d[2];
				mzLocalVertex[5] = d; d[1] = - d[1];
				mzLocalVertex[6] = d; d[2] = - d[2];
				mzLocalVertex[7] = d;

				Create(8);
			};
		};
		
		class Point : public PhysicalModel
		{
		public:
			Point(tScalar mass = 1.0)
				: PhysicalModel(0){
				mzLocalVertex = new tVector[1];
				mNumVertices = 1;

				mMassQuot = 1.0;
				mLocalInverseInertiaTensor = mLocalInverseInertiaTensor.identity();
			};
		};
		
#elif defined(__PHYSICS_BULLET__)

		/**** Bullet physics ****/
		
		class Cylinder : public PhysicalModel
		{
			public:
				Cylinder(const StringPool::Entry& sName, tScalar r, tScalar h, unsigned detail = 0):
					PhysicalModel(sName, new btCylinderShape(btVector3(r, h / 2, r)), 1.0) {
					
				};
				virtual ~Cylinder() {};

				DATA_LOADABLE(Cylinder, GC_ADDEDSIZE_AUTO(PhysicalModel, Cylinder));
		};
		
		class Sphere : public PhysicalModel
		{
			public:
				Sphere(const StringPool::Entry& sName, tScalar r, unsigned detail = 0):
					PhysicalModel(sName, new btSphereShape(r), 1.0) {
				};
				virtual ~Sphere() {};

				DATA_LOADABLE(Sphere, GC_ADDEDSIZE_AUTO(PhysicalModel, Sphere));
		};

		class Oblong : public PhysicalModel
		{
			public:
				Oblong(const StringPool::Entry& sName, const tVector& dim, tScalar mass = 1.0)
				: PhysicalModel(sName, new btBoxShape(FROMVECT(dim * 0.5)), 1 / mass){
				};
				virtual ~Oblong() {};

				DATA_LOADABLE(Oblong, GC_ADDEDSIZE_AUTO(PhysicalModel, Oblong));
		};
		
		class Capsule : public PhysicalModel {
			public:
				Capsule(const StringPool::Entry& sName, const tVector& dim, tScalar mass = 1.0, tScalar r = 0.0)
				: PhysicalModel(sName, new btCapsuleShape(r == 0.0 ? (dim[0] + dim[2]) / 2.0 : r, dim[1]), mass)
				{ };
				virtual ~Capsule() {};

				DATA_LOADABLE(Capsule, GC_ADDEDSIZE_AUTO(PhysicalModel, Capsule));
		};

		class Point : public PhysicalModel
		{
			public:
				Point(const StringPool::Entry& sName, tScalar mass = 1.0)
				: PhysicalModel(sName, new btSphereShape(0.01), 1 / mass){
				};
				virtual ~Point() {};

				DATA_LOADABLE(Point, GC_ADDEDSIZE_AUTO(PhysicalModel, Point));
		};

		class Compound : public PhysicalModel
		{
			std::vector<PhysicalModel::tRef> mvModels;

		public:
			Compound(const StringPool::Entry& sName, tScalar mass = 1.0)
			: PhysicalModel(sName, new btCompoundShape(), 1 / mass) {
			};
			virtual ~Compound() {};

			void InsertModel(const PhysicalModel::tRef& rModel, const tVector& vPos, const Orientation3D& ori) {
				btCompoundShape * pModel = reinterpret_cast<btCompoundShape *>(getCollisionShape());
				pModel->addChildShape(btTransform(FROMORI(ori), FROMVECT(vPos)), rModel->getCollisionShape());
				mvModels.push_back(rModel);

				// Recalc inertial tensor
				if(getMassQuot() != 0) {
					btVector3 v;
					pModel->calculateLocalInertia(1.0 / getMassQuot(), v);
					setLocalInertia(v);
				};
			};

			DATA_LOADABLE(Compound, GC_ADDEDSIZE_AUTO(PhysicalModel, Compound));
		};

#else
	
		/**** No physics ****/
		
		class Cylinder : public PhysicalModel
		{
			public:
				Cylinder(tScalar r, tScalar h, unsigned detail = 0):
					PhysicalModel() {
				};
		};
		
		class Sphere : public PhysicalModel
		{
			public:
				Sphere(tScalar r, unsigned detail = 0):
					PhysicalModel() {
				};
		};

		class Oblong : public PhysicalModel
		{
			public:
				Oblong(const tVector& dim)
				: PhysicalModel(){
				};
		};
		
		class Point : public PhysicalModel
		{
			public:
				Point(tScalar mass = 1.0)
				: PhysicalModel(){
				};
		};

#endif

		class Cube : public Oblong
		{
			public:
				Cube(const StringPool::Entry& sName, const tScalar hl): Oblong(sName, tVector(hl * 2, hl * 2, hl * 2)){			
				};

				DATA_LOADABLE(Cube, GC_ADDEDSIZE_AUTO(Oblong, Cube));
		};

	};
};

#endif

